using System;
using System.Collections.Generic;
using System.Text;

namespace ASMdll
{
	internal static class TwoOperandsTranslator
	{
		static int regSet = (1 << 16) - 1;

		static int regSetByte = (1 | 4 | 16 | 64 | 256 | 1024 | 4096 | 16384);

		static int sregSet = ((1 << 6) - 1) << 16;

		private static Byte[] Parse_ADD_SUB_AND_OR_XOR_CMP(Byte T1, Byte T2, Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			#region ADD_SUB_AND_OR_XOR_CMP R/M, IM

			if (op2.AT == Operand.AddressingType.Immediate) // ..., IM
			{
				Operand.Size sz;

				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Error in operand!");

				#region ADD_SUB_AND_OR_XOR_CMP R, IM

				if (op1.AT == Operand.AddressingType.Register) // SUB R, IM
				{
					if (op2.Size_ != Operand.Size.Undefined && op2.Size_ != Operand.getSize(op1.R))
						throw new Exception("Operands have different sizes!");

					if (Operand.getSize(op1.R) == Operand.Size.Byte && op2.Size_ == Operand.Size.Undefined && op2.IM > 255)
						throw new Exception("Operands have different sizes!");

					sz = Operand.getSize(op1.R);

					if (sz == Operand.Size.Byte)
					{
						code = new Byte[3];
						code[0] = (Byte)((8 << 4));
						code[1] = Operand.GetModRMByte(op1);
						code[1] |= T2;
						code[2] = (Byte)op2.IM;
					}

					if (sz == Operand.Size.Word)
					{
						code = new Byte[4];
						code[0] = (Byte)((8 << 4) | 1);
						code[1] = Operand.GetModRMByte(op1);
						code[1] |= T2;
						code[2] = (Byte)(op2.IM & 255);
						code[3] = (Byte)(op2.IM >> 8);
					}

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				#endregion

				#region ADD_SUB_AND_OR_XOR_CMP M, IM

				if (op1.AT == Operand.AddressingType.Based) // SUB M, IM
				{
					if (op2.Size_ == Operand.Size.Undefined)
						throw new Exception("Error in operand, unknown size!");

					sz = op2.Size_;

					if (sz == Operand.Size.Byte)
					{
						code = new Byte[3];
						code[0] = (Byte)((8 << 4));
						code[1] = Operand.GetModRMByte(op1);
						code[1] |= T2;
						code[2] = (Byte)op2.IM;
					}
					else
					{
						code = new Byte[4];
						code[0] = (Byte)((8 << 4) | 1);
						code[1] = Operand.GetModRMByte(op1);
						code[1] |= T2;
						code[2] = (Byte)(op2.IM & 255);
						code[3] = (Byte)(op2.IM >> 8);
					}

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				#endregion

				#region ADD_SUB_AND_OR_XOR_CMP M, IM

				if (op2.Size_ == Operand.Size.Undefined)
					throw new Exception("Error in operand, unknown size!");

				sz = op2.Size_;

				needWriteMemory = true;
				byteIndToWriteMemory = 2;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				if (sz == Operand.Size.Byte)
				{
					code = new Byte[5];
					code[0] = (Byte)(8 << 4);
					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T2;
					code[4] = (Byte)op2.IM;
				}
				else
				{
					code = new Byte[6];
					code[0] = (Byte)((8 << 4) | 1);
					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T2;
					code[4] = (Byte)(op2.IM & 255);
					code[5] = (Byte)(op2.IM >> 8);
				}

				return code;

				#endregion
			}

			#endregion

			#region ADD_SUB_AND_OR_XOR_CMP R, R

			if (op1.AT == Operand.AddressingType.Register && op2.AT == Operand.AddressingType.Register)
			{
				if (Operand.getSize(op1.R) != Operand.getSize(op2.R))
					throw new Exception("Operands have differenr sizes!");

				if (((int)op1.R & regSet) == 0 || ((int)op2.R & regSet) == 0)
					throw new Exception("Not valid register type!");

				Operand.Size sz = Operand.getSize(op1.R);

				code = new Byte[2];
				code[0] = T1;
				if (sz == Operand.Size.Word)
					code[0] |= 1;

				code[1] = Operand.GetModRMByte(op1);
				code[1] |= (Byte)(Operand.getRegBits(op2.R) << 3);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region ADD_SUB_AND_OR_XOR_CMP M, R

			if (op2.AT == Operand.AddressingType.Register)
			{
				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Error in operands!");

				if (op1.AT == Operand.AddressingType.Based)
				{
					code = new Byte[2];

					code[0] = T1;
					if (Operand.getSize(op2.R) == Operand.Size.Word)
						code[0] |= 1;
					code[1] = Operand.GetModRMByte(op1);
					code[1] |= (Byte)(Operand.getRegBits(op2.R) << 3);

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				code = new Byte[4];

				code[0] = T1;
				if (Operand.getSize(op2.R) == Operand.Size.Word)
					code[0] |= 1;
				code[1] = Operand.GetModRMByte(op1);
				code[1] |= (Byte)(Operand.getRegBits(op2.R) << 3);

				needWriteMemory = true;
				byteIndToWriteMemory = 2;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region ADD_SUB_AND_OR_XOR_CMP R, M

			if (op1.AT == Operand.AddressingType.Register)
			{
				if (op2.AT == Operand.AddressingType.Immediate)
					throw new Exception("Internal compiler error!");

				if (op2.AT == Operand.AddressingType.Based)
				{
					code = new Byte[2];

					code[0] = (Byte)(T1 | 2);
					if (Operand.getSize(op1.R) == Operand.Size.Word)
						code[0] |= 1;
					code[1] = Operand.GetModRMByte(op2);
					code[1] |= (Byte)(Operand.getRegBits(op1.R) << 3);

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				code = new Byte[4];

				code[0] = (Byte)(T1 | 2);
				if (Operand.getSize(op1.R) == Operand.Size.Word)
					code[0] |= 1;
				code[1] = Operand.GetModRMByte(op2);
				code[1] |= (Byte)(Operand.getRegBits(op1.R) << 3);

				needWriteMemory = true;
				byteIndToWriteMemory = 2;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			throw new Exception("Error in operands!");
		}


		public static Byte[] ParseMOV(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			if (op1.AT == Operand.AddressingType.Immediate)
				throw new Exception("Error in operands!");

			#region MOV R/M, IM
			if (op2.AT == Operand.AddressingType.Immediate) // MOV ..., IM
			{
				if (op1.AT == Operand.AddressingType.Register) // MOV R, IM
				{
					if ((regSet & (int)op1.R) != 0)
					{
						if (op2.Size_ != Operand.Size.Undefined && op2.Size_ != Operand.getSize(op1.R))
							throw new Exception("Operands of operator MOV have different size!");

						Operand.Size sz = Operand.getSize(op1.R);
						if (sz == Operand.Size.Byte)
						{
							code = new Byte[2];
							if (op2.IM > 255)
								throw new Exception("Operands of operator MOV have different size!");
							code[1] = (Byte)op2.IM;
						}
						else
						{
							code = new Byte[3];
							code[1] = (Byte)(op2.IM & 255);
							code[2] = (Byte)(op2.IM >> 8);
						}

						code[0] = (11 << 4);
						if (sz == Operand.Size.Word)
							code[0] |= (1 << 3);
						code[0] |= Operand.getRegBits(op1.R);

						needWriteMemory = false;
						byteIndToWriteMemory = -1;
						needWriteD8 = false;
						byteToWriteD8 = -1;
						needWriteD16 = false;
						byteToWriteD16 = -1;

						return code;
					}

					throw new Exception("Invalid register in operator MOV!");
				}

				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Invalid operands in operator MOV!");

				// MOV M, IM

				if (op2.Size_ == Operand.Size.Undefined)
					throw new Exception("Impossible to find out the size of operands!");


				if (op1.AT == Operand.AddressingType.RegisterImplicit)
				{
					if (op2.Size_ == Operand.Size.Byte)
						code = new Byte[3];
					if (op2.Size_ == Operand.Size.Word)
						code = new Byte[4];
					if (op2.Size_ == Operand.Size.Byte)
						code[2] = (Byte)op2.IM;
					if (op2.Size_ == Operand.Size.Word)
					{
						code[2] = (Byte)(op2.IM & 255);
						code[3] = (Byte)(op2.IM >> 8);
					}
				}
				else
				{
					if (op2.Size_ == Operand.Size.Byte)
						code = new Byte[5];
					if (op2.Size_ == Operand.Size.Word)
						code = new Byte[6];
					if (op2.Size_ == Operand.Size.Byte)
						code[4] = (Byte)op2.IM;
					if (op2.Size_ == Operand.Size.Word)
					{
						code[4] = (Byte)(op2.IM & 255);
						code[5] = (Byte)(op2.IM >> 8);
					}
				}

				code[0] = (12 << 4) | 6;
				if (op2.Size_ == Operand.Size.Word)
					code[0] |= 1;
				code[1] = Operand.GetModRMByte(op1);

				if (op1.AT == Operand.AddressingType.Pointer || op1.AT == Operand.AddressingType.PointerWithIndexing)
				{
					needWriteMemory = true;
					byteIndToWriteMemory = 2;
				}
				else
				{
					if (op1.AT == Operand.AddressingType.Based)
					{
						code[2] = (Byte)(op1.Offset & 255);
						code[3] = (Byte)(op1.Offset >> 8);
					}
					needWriteMemory = false;
					byteIndToWriteMemory = -1;
				}
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}
			#endregion

			#region MOV SR, R/M
			if (op1.AT == Operand.AddressingType.Register && ((int)op1.R & sregSet) != 0) // MOV SR, ...
			{
				if (op2.AT == Operand.AddressingType.Immediate)
					throw new Exception("Invalid operand!");

				if (op2.AT == Operand.AddressingType.Register) // MOV SR, R
				{
					if(((int)op2.R & regSet) == 0 || ((int)op2.R & regSetByte) != 1)
						throw new Exception("Invalid operand!");

					code = new Byte[2];
					code[0] = (Byte)((8 << 4) | 14);
					code[1] = (Byte)((3 << 6) | (Operand.getSregBits(op1.R) << 3) | Operand.getRegBits(op2.R));

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;
					return code;
				}

				// MOV SR, M

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				if (op1.AT == Operand.AddressingType.RegisterImplicit)
					code = new Byte[2];
				else
					code = new Byte[4];

				code[0] = (Byte)((8 << 4) | 14);
				code[1] = (Byte)(Operand.GetModRMByte(op2) | (Operand.getSregBits(op1.R) << 3));

				if (op1.AT == Operand.AddressingType.Based)
				{
					code[2] = (Byte)(op1.Offset & 255);
					code[3] = (Byte)(op1.Offset >> 8);
				}
				if (op1.AT == Operand.AddressingType.PointerWithIndexing || op1.AT == Operand.AddressingType.Pointer)
				{
					needWriteMemory = true;
					byteIndToWriteMemory = 2;
				}
				return code;

			}
			#endregion

			#region MOV R/M, SR
			if (op2.AT == Operand.AddressingType.Register && ((int)op2.R & sregSet) != 0) // MOV ..., S/R
			{
				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Invalid operand!");

				if (op1.AT == Operand.AddressingType.Register) // MOV R, SR
				{
					if (((int)op1.R & regSet) == 0 || ((int)op1.R & regSetByte) != 1)
						throw new Exception("Invalid operand!");

					code = new Byte[2];
					code[0] = (Byte)((8 << 4) | 12);
					code[1] = (Byte)((3 << 6) | (Operand.getSregBits(op2.R) << 3) | Operand.getRegBits(op1.R));

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;
					return code;
				}

				// MOV M, SR

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				if (op2.AT == Operand.AddressingType.RegisterImplicit)
					code = new Byte[2];
				else
					code = new Byte[4];

				code[0] = (Byte)((8 << 4) | 12);
				code[1] = (Byte)(Operand.GetModRMByte(op1) | (Operand.getSregBits(op2.R) << 3));

				if (op2.AT == Operand.AddressingType.Based)
				{
					code[2] = (Byte)(op2.Offset & 255);
					code[3] = (Byte)(op2.Offset >> 8);
				}
				if (op2.AT == Operand.AddressingType.PointerWithIndexing || op2.AT == Operand.AddressingType.Pointer)
				{
					needWriteMemory = true;
					byteIndToWriteMemory = 2;
				}
				return code;

			}
			#endregion

			#region MOV R, R/M
			if (op1.AT == Operand.AddressingType.Register) // MOV R, ...
			{
				if (op2.AT == Operand.AddressingType.Immediate)
					throw new Exception("Internal compiler error!");

				if (op2.AT == Operand.AddressingType.Register) // MOV R, R
				{
					if (((int)op2.R & regSet) == 0 || Operand.getSize(op1.R) != Operand.getSize(op2.R))
						throw new Exception("Error in operands!");

					code = new Byte[2];
					code[0] = (8 << 4) | 10;
					if (Operand.getSize(op1.R) == Operand.Size.Word)
						code[0] |= 1;

					code[1] = Operand.GetModRMByte(op2);
					code[1] |= (Byte)(Operand.getRegBits(op1.R) << 3);

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;
					return code;
				}

				// MOV R, M

				if (op2.AT == Operand.AddressingType.RegisterImplicit)
					code = new Byte[2];
				else
					code = new Byte[4];

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				code[0] = (8 << 4) | 10;
				if (Operand.getSize(op1.R) == Operand.Size.Word)
					code[0] |= 1;

				code[1] = Operand.GetModRMByte(op2);
				code[1] |= (Byte)(Operand.getRegBits(op1.R) << 3);

				if (op2.AT == Operand.AddressingType.Pointer || op2.AT == Operand.AddressingType.PointerWithIndexing)
				{
					needWriteMemory = true;
					byteIndToWriteMemory = 2;
				}

				if (op2.AT == Operand.AddressingType.Based)
				{
					code[2] = (Byte)(op2.Offset & 255);
					code[3] = (Byte)(op2.Offset >> 8);
				}

				return code;
			}
			#endregion

			#region MOV M, R

			// MOV M, R
			if (op2.AT != Operand.AddressingType.Register)
				throw new Exception("Error in operands!");



			// MOV M, R

			if (op1.AT == Operand.AddressingType.RegisterImplicit)
				code = new Byte[2];
			else
				code = new Byte[4];

			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			code[0] = (8 << 4) | 8;
			if (Operand.getSize(op2.R) == Operand.Size.Word)
				code[0] |= 1;

			code[1] = Operand.GetModRMByte(op1);
			code[1] |= (Byte)(Operand.getRegBits(op2.R) << 3);

			if (op1.AT == Operand.AddressingType.Pointer || op1.AT == Operand.AddressingType.PointerWithIndexing)
			{
				needWriteMemory = true;
				byteIndToWriteMemory = 2;
			}

			if (op1.AT == Operand.AddressingType.Based)
			{
				code[2] = (Byte)(op1.Offset & 255);
				code[3] = (Byte)(op1.Offset >> 8);
			}

			return code;
			#endregion
		}

		public static Byte[] ParseLEA(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Register)
				throw new Exception("Error in operand!");
			if (((int)op1.R & regSet) == 0 || Operand.getSize(op1.R) != Operand.Size.Word)
				throw new Exception("Error in operand!");
			if (op2.AT == Operand.AddressingType.Register || op2.AT == Operand.AddressingType.Immediate)
				throw new Exception("Error in operand!");

			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			if (op2.AT == Operand.AddressingType.RegisterImplicit)
				code = new Byte[2];
			else
				code = new Byte[4];

			code[0] = (Byte)((8 << 4) | 13);
			code[1] = Operand.GetModRMByte(op2);
			code[1] |= (Byte)(Operand.getRegBits(op1.R) << 3);

			if (op2.AT == Operand.AddressingType.Based)
			{
				code[2] = (Byte)(op2.Offset & 255);
				code[3] = (Byte)(op2.Offset >> 8);
			}
			if (op2.AT == Operand.AddressingType.Pointer || op2.AT == Operand.AddressingType.PointerWithIndexing)
			{
				needWriteMemory = true;
				byteIndToWriteMemory = 2;
			}

			return code;
		}

		public static Byte[] ParseADD(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			return Parse_ADD_SUB_AND_OR_XOR_CMP(0, 0, op1, op2, ref needWriteMemory, ref byteIndToWriteMemory, ref needWriteD8, ref byteToWriteD8, ref needWriteD16, ref byteToWriteD16);
		}

		public static Byte[] ParseSUB(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			return Parse_ADD_SUB_AND_OR_XOR_CMP((Byte)((2 << 4) | 8), (Byte)(5 << 3), op1, op2, ref needWriteMemory, ref byteIndToWriteMemory, ref needWriteD8, ref byteToWriteD8, ref needWriteD16, ref byteToWriteD16);
		}

		public static Byte[] ParseAND(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			return Parse_ADD_SUB_AND_OR_XOR_CMP((Byte)((2 << 4)), (Byte)(4 << 3), op1, op2, ref needWriteMemory, ref byteIndToWriteMemory, ref needWriteD8, ref byteToWriteD8, ref needWriteD16, ref byteToWriteD16);
		}

		public static Byte[] ParseOR(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			return Parse_ADD_SUB_AND_OR_XOR_CMP((Byte)(8), (Byte)(1 << 3), op1, op2, ref needWriteMemory, ref byteIndToWriteMemory, ref needWriteD8, ref byteToWriteD8, ref needWriteD16, ref byteToWriteD16);
		}

		public static Byte[] ParseXOR(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			return Parse_ADD_SUB_AND_OR_XOR_CMP((Byte)((3 << 4)), (Byte)(6 << 3), op1, op2, ref needWriteMemory, ref byteIndToWriteMemory, ref needWriteD8, ref byteToWriteD8, ref needWriteD16, ref byteToWriteD16);
		}

		public static Byte[] ParseCMP(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			return Parse_ADD_SUB_AND_OR_XOR_CMP((Byte)((3 << 4) | 8), (Byte)(7 << 3), op1, op2, ref needWriteMemory, ref byteIndToWriteMemory, ref needWriteD8, ref byteToWriteD8, ref needWriteD16, ref byteToWriteD16);
		}

		public static Byte[] ParseXCHG(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			if (op1.AT == Operand.AddressingType.Immediate || op2.AT == Operand.AddressingType.Immediate)
				throw new Exception("Error in operand!");
			if (op1.AT != Operand.AddressingType.Register && op2.AT != Operand.AddressingType.Register)
				throw new Exception("Error in operand!");

			Operand.Size sz;

			if (op1.AT != Operand.AddressingType.Register)
			{
				Operand op = op1;
				op1 = op2;
				op2 = op;
			}

			if (op2.AT == Operand.AddressingType.Register) // XCHG R, R
			{
				if (Operand.getSize(op1.R) != Operand.getSize(op2.R))
					throw new Exception("Operands have different sizes!");

				sz = Operand.getSize(op1.R);

				code = new Byte[2];

				code[0] = (Byte)((8 << 4) | 6);
				if (sz == Operand.Size.Word)
					code[0] |= 1;

				code[1] = Operand.GetModRMByte(op2);
				code[1] |= (Byte)(Operand.getRegBits(op1.R) << 3);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			if (op2.AT == Operand.AddressingType.RegisterImplicit) // XCHG R, M
			{
				sz = Operand.getSize(op1.R);
				code = new Byte[2];
				
				code[0] = (Byte)((8 << 4) | 6);
				if (sz == Operand.Size.Word)
					code[0] |= 1;

				code[1] = Operand.GetModRMByte(op2);
				code[1] |= (Byte)(Operand.getRegBits(op1.R) << 3);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			sz = Operand.getSize(op1.R);
			code = new Byte[4];

			code[0] = (Byte)((8 << 4) | 6);
			if (sz == Operand.Size.Word)
				code[0] |= 1;

			code[1] = Operand.GetModRMByte(op2);
			code[1] |= (Byte)(Operand.getRegBits(op1.R) << 3);

			needWriteMemory = true;
			byteIndToWriteMemory = 2;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			return code;

		}

		public static Byte[] ParseTEST(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			#region TEST R/M, R

			if (op2.AT == Operand.AddressingType.Register) // TEST ..., R
			{
				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Error in operands!");

				if (op1.AT == Operand.AddressingType.Register) // TEST R, R
				{
					if(Operand.getSize(op1.R) != Operand.getSize(op2.R))
						throw new Exception("Operands have different sizes!");

					if(((int)op1.R & regSet) == 0 || ((int)op2.R & regSet) == 0)
						throw new Exception("Error in operands!");

					code = new Byte[2];

					code[0] = (Byte)((8 << 4) | 4);
					if (Operand.getSize(op1.R) == Operand.Size.Word)
						code[0] |= 1;
					code[1] = Operand.GetModRMByte(op1);
					code[1] |= (Byte)(Operand.getRegBits(op2.R) << 3);

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				if (op1.AT == Operand.AddressingType.RegisterImplicit) // TEST M, R
				{
					if (((int)op2.R & regSet) == 0)
						throw new Exception("Error in operands!");

					code = new Byte[2];

					code[0] = (Byte)((8 << 4) | 4);
					if (Operand.getSize(op2.R) == Operand.Size.Word)
						code[0] |= 1;
					code[1] = Operand.GetModRMByte(op1);
					code[1] |= (Byte)(Operand.getRegBits(op2.R) << 3);

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				if (((int)op2.R & regSet) == 0)
					throw new Exception("Error in operands!");

				// TEST M, R

				code = new Byte[4];

				code[0] = (Byte)((8 << 4) | 4);
				if (Operand.getSize(op2.R) == Operand.Size.Word)
					code[0] |= 1;
				code[1] = Operand.GetModRMByte(op1);
				code[1] |= (Byte)(Operand.getRegBits(op2.R) << 3);

				needWriteMemory = true;
				byteIndToWriteMemory = 2;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region TEST R/M, IM

			if (op2.AT == Operand.AddressingType.Immediate) // TEST ..., IM
			{
				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Error in operands!");

				if (op1.AT == Operand.AddressingType.Register) // TEST R, IM
				{
					if (op2.Size_ != Operand.Size.Undefined && Operand.getSize(op1.R) != op2.Size_)
						throw new Exception("Operands have different sizes!");

					if (((int)op1.R & regSet) == 0)
						throw new Exception("Error in operands!");

					if (Operand.getSize(op1.R) == Operand.Size.Byte)
					{
						if (op2.IM > 255)
							throw new Exception("Operands have different sizes!");
						code = new Byte[3];
						code[0] = (Byte)((15 << 4) | 6);
						code[1] = (Byte)(Operand.GetModRMByte(op1) | (3 << 3));
						code[2] = (Byte)op2.IM;
					}
					else
					{
						code = new Byte[4];
						code[0] = (Byte)((15 << 4) | 7);
						code[1] = (Byte)(Operand.GetModRMByte(op1) | (3 << 3));
						code[2] = (Byte)(op2.IM & 255);
						code[3] = (Byte)(op2.IM >> 8);
					}

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				if (op1.AT == Operand.AddressingType.RegisterImplicit) // TEST M, IM
				{
					if (op2.Size_ == Operand.Size.Undefined)
						throw new Exception("Unknown size of operands!");

					if (op2.Size_ == Operand.Size.Byte)
					{
						code = new Byte[3];
						code[0] = (Byte)((15 << 4) | 6);
						code[1] = (Byte)(Operand.GetModRMByte(op1) | (3 << 3));
						code[2] = (Byte)op2.IM;
					}
					else
					{
						code = new Byte[4];
						code[0] = (Byte)((15 << 4) | 7);
						code[1] = (Byte)(Operand.GetModRMByte(op1) | (3 << 3));
						code[2] = (Byte)(op2.IM & 255);
						code[3] = (Byte)(op2.IM >> 8);
					}

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				// TEST M, IM

				if (op2.Size_ == Operand.Size.Undefined)
					throw new Exception("Unknown size of operands!");

				if (op2.Size_ == Operand.Size.Byte)
				{
					code = new Byte[5];
					code[0] = (Byte)((15 << 4) | 6);
					code[1] = (Byte)(Operand.GetModRMByte(op1) | (3 << 3));
					code[4] = (Byte)op2.IM;
				}
				else
				{
					code = new Byte[6];
					code[0] = (Byte)((15 << 4) | 7);
					code[1] = (Byte)(Operand.GetModRMByte(op1) | (3 << 3));
					code[4] = (Byte)(op2.IM & 255);
					code[5] = (Byte)(op2.IM >> 8);
				}

				needWriteMemory = true;
				byteIndToWriteMemory = 2;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}
			#endregion

			throw new Exception("Error in operands!");
		}

		public static Byte[] ParseSHL(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			Byte T = (Byte)(4 << 3);

			#region SHL R/M, 1

			if (op2.AT == Operand.AddressingType.Immediate && op2.IM == 1)
			{
				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Error in operands!");

				if (op1.AT == Operand.AddressingType.Register) // SHL R, 1
				{
					if(((int)op1.R & regSet) == 0)
						throw new Exception("Error in operands!");

					code = new Byte[2];

					code[0] = (Byte)((13 << 4));
					if (Operand.getSize(op1.R) == Operand.Size.Word)
						code[0] |= 1;

					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				if (op1.AT == Operand.AddressingType.RegisterImplicit) // SHL M, 1
				{
					code = new Byte[2];

					code[0] = (Byte)((13 << 4) | 1);
					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				code = new Byte[4];

				code[0] = (Byte)((13 << 4) | 1);
				code[1] = Operand.GetModRMByte(op1);
				code[1] |= T;

				needWriteMemory = true;
				byteIndToWriteMemory = 2;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region SHL R/M, CL

			if (op2.AT == Operand.AddressingType.Register && op2.R == Operand.Register.CL)
			{
				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Error in operands!");

				if (op1.AT == Operand.AddressingType.Register) // SHL R, CL
				{
					if (((int)op1.R & regSet) == 0)
						throw new Exception("Error in operands!");

					code = new Byte[2];

					code[0] = (Byte)((13 << 4) | 2);
					if (Operand.getSize(op1.R) == Operand.Size.Word)
						code[0] |= 1;

					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				if (op1.AT == Operand.AddressingType.RegisterImplicit) // SHL M, CL
				{
					code = new Byte[2];

					code[0] = (Byte)((13 << 4) | 3);
					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				code = new Byte[4];

				code[0] = (Byte)((13 << 4) | 3);
				code[1] = Operand.GetModRMByte(op1);
				code[1] |= T;

				needWriteMemory = true;
				byteIndToWriteMemory = 2;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region SHL M/R, IM

			if (op2.AT == Operand.AddressingType.Immediate)
			{
				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Error in operands!");

				if (op1.AT == Operand.AddressingType.Register) // SHL R, IM
				{
					if (((int)op1.R & regSet) == 0)
						throw new Exception("Error in operands!");

					if (op1.Size_ == Operand.Size.Word || (op1.Size_ == Operand.Size.Undefined && op1.IM > 255))
						throw new Exception("Error in operands!");

					code = new Byte[3];

					code[0] = (Byte)((12 << 4));
					if (Operand.getSize(op1.R) == Operand.Size.Word)
						code[0] |= 1;

					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					code[2] = (Byte)op2.IM;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				if (op1.AT == Operand.AddressingType.RegisterImplicit) // SHL M, IM
				{
					if (op1.Size_ == Operand.Size.Word || (op1.Size_ == Operand.Size.Undefined && op1.IM > 255))
						throw new Exception("Error in operands!");

					code = new Byte[3];

					code[0] = (Byte)((12 << 4));
					if (Operand.getSize(op1.R) == Operand.Size.Word)
						code[0] |= 1;

					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					code[2] = (Byte)op2.IM;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				if (op1.Size_ == Operand.Size.Word || (op1.Size_ == Operand.Size.Undefined && op1.IM > 255))
					throw new Exception("Error in operands!");

				code = new Byte[5];

				code[0] = (Byte)((12 << 4));
				if (Operand.getSize(op1.R) == Operand.Size.Word)
					code[0] |= 1;

				code[1] = Operand.GetModRMByte(op1);
				code[1] |= T;

				code[4] = (Byte)op2.IM;

				needWriteMemory = true;
				byteIndToWriteMemory = 2;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			throw new Exception("Error in operands!");
		}

		public static Byte[] ParseSHR(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			Byte T = (Byte)(5 << 3);

			#region SHR R/M, 1

			if (op2.AT == Operand.AddressingType.Immediate && op2.IM == 1)
			{
				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Error in operands!");

				if (op1.AT == Operand.AddressingType.Register) // SHR R, 1
				{
					if (((int)op1.R & regSet) == 0)
						throw new Exception("Error in operands!");

					code = new Byte[2];

					code[0] = (Byte)((13 << 4));
					if (Operand.getSize(op1.R) == Operand.Size.Word)
						code[0] |= 1;

					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				if (op1.AT == Operand.AddressingType.RegisterImplicit) // SHR M, 1
				{
					code = new Byte[2];

					code[0] = (Byte)((13 << 4) | 1);
					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				code = new Byte[4];

				code[0] = (Byte)((13 << 4) | 1);
				code[1] = Operand.GetModRMByte(op1);
				code[1] |= T;

				needWriteMemory = true;
				byteIndToWriteMemory = 2;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region SHR R/M, CL

			if (op2.AT == Operand.AddressingType.Register && op2.R == Operand.Register.CL)
			{
				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Error in operands!");

				if (op1.AT == Operand.AddressingType.Register) // SHR R, CL
				{
					if (((int)op1.R & regSet) == 0)
						throw new Exception("Error in operands!");

					code = new Byte[2];

					code[0] = (Byte)((13 << 4) | 2);
					if (Operand.getSize(op1.R) == Operand.Size.Word)
						code[0] |= 1;

					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				if (op1.AT == Operand.AddressingType.RegisterImplicit) // SHR M, CL
				{
					code = new Byte[2];

					code[0] = (Byte)((13 << 4) | 3);
					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				code = new Byte[4];

				code[0] = (Byte)((13 << 4) | 3);
				code[1] = Operand.GetModRMByte(op1);
				code[1] |= T;

				needWriteMemory = true;
				byteIndToWriteMemory = 2;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region SHR M/R, IM

			if (op2.AT == Operand.AddressingType.Immediate)
			{
				if (op1.AT == Operand.AddressingType.Immediate)
					throw new Exception("Error in operands!");

				if (op1.AT == Operand.AddressingType.Register) // SHR R, IM
				{
					if (((int)op1.R & regSet) == 0)
						throw new Exception("Error in operands!");

					if (op1.Size_ == Operand.Size.Word || (op1.Size_ == Operand.Size.Undefined && op1.IM > 255))
						throw new Exception("Error in operands!");

					code = new Byte[3];

					code[0] = (Byte)((12 << 4));
					if (Operand.getSize(op1.R) == Operand.Size.Word)
						code[0] |= 1;

					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					code[2] = (Byte)op2.IM;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				if (op1.AT == Operand.AddressingType.RegisterImplicit) // SHR M, IM
				{
					if (op1.Size_ == Operand.Size.Word || (op1.Size_ == Operand.Size.Undefined && op1.IM > 255))
						throw new Exception("Error in operands!");

					code = new Byte[3];

					code[0] = (Byte)((12 << 4));
					if (Operand.getSize(op1.R) == Operand.Size.Word)
						code[0] |= 1;

					code[1] = Operand.GetModRMByte(op1);
					code[1] |= T;

					code[2] = (Byte)op2.IM;

					needWriteMemory = false;
					byteIndToWriteMemory = -1;
					needWriteD8 = false;
					byteToWriteD8 = -1;
					needWriteD16 = false;
					byteToWriteD16 = -1;

					return code;
				}

				if (op1.Size_ == Operand.Size.Word || (op1.Size_ == Operand.Size.Undefined && op1.IM > 255))
					throw new Exception("Error in operands!");

				code = new Byte[5];

				code[0] = (Byte)((12 << 4));
				if (Operand.getSize(op1.R) == Operand.Size.Word)
					code[0] |= 1;

				code[1] = Operand.GetModRMByte(op1);
				code[1] |= T;

				code[4] = (Byte)op2.IM;

				needWriteMemory = true;
				byteIndToWriteMemory = 2;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			throw new Exception("Error in operands!");
		}

		public static Byte[] ParseIMUL(Operand op1, Operand op2, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Register)
				throw new Exception("Error in operands!");

			if (((int)op1.R & 255) == 0)
				throw new Exception("Error in operands!");

			if (Operand.getSize(op1.R) == Operand.Size.Byte)
				throw new Exception("Error in operand! Registers must be 16-bit sized!");

			if (op2.AT == Operand.AddressingType.Immediate)
				throw new Exception("Error in operands!");

			#region IMUL R, R

			if (op2.AT == Operand.AddressingType.Register)
			{
				if(((int)op2.R & 255) == 0)
					throw new Exception("Error in operands!");

				if (Operand.getSize(op1.R) != Operand.getSize(op2.R))
					throw new Exception("Error in operands!");

				code = new Byte[3];

				code[0] = 15;
				code[1] = (Byte)((10 << 4) | 15);
				code[2] = Operand.GetModRMByte(op2);
				code[2] |= (Byte)(Operand.getRegBits(op1.R) << 3);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region IMUL R, M

			if (op2.AT == Operand.AddressingType.RegisterImplicit)
			{
				code = new Byte[3];
				
				code[0] = 15;
				code[1] = (Byte)((10 << 4) | 15);
				code[2] = Operand.GetModRMByte(op2);
				code[2] |= (Byte)(Operand.getRegBits(op1.R) << 3);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region IMUL R, M

			code = new Byte[5];

			code[0] = 15;
			code[1] = (Byte)((10 << 4) | 15);
			code[2] = Operand.GetModRMByte(op2);
			code[2] |= (Byte)(Operand.getRegBits(op1.R) << 3);

			needWriteMemory = true;
			byteIndToWriteMemory = 2;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			return code;

			#endregion
		}
	}
}
